﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TypeResolve
{
	public static class TypeNameConverter
	{
		private static readonly Dictionary<string, string> predefinedNames = new Dictionary<string, string>();

		static TypeNameConverter()
		{
			foreach (var mapping in TypeResolver.predefinedMappings)
			{
				predefinedNames.Add(mapping.Value.FullName, mapping.Key);
			}
		}

		public static string ToShortTypeName(string fullTypeName)
		{
			fullTypeName = ExtractFullNameFromFullyQualifiedName(fullTypeName);
			var indexOfGenericSign = fullTypeName.IndexOf("`");
			var indexOfBracket = fullTypeName.IndexOf("[");
			var isGenericType = (indexOfGenericSign != -1);
			var isArrayType = !isGenericType && (indexOfBracket != -1);

			var typename = new StringBuilder("");
			if (isArrayType)
			{
				var arrayType = ToShortTypeName(fullTypeName.Substring(0, indexOfBracket));
				var arrayRank = fullTypeName.Substring(indexOfBracket - 1);
				typename.Append(arrayType);
				typename.Append(ConvertArrayRank(arrayRank));
			}
			else if (isGenericType)
			{
				var isGenericTypeDef = (indexOfBracket == -1);
				if (isGenericTypeDef)
				{
					typename.Append(CheckForPredefinedName(fullTypeName));
				}
				else
				{
					var typeNameOnly = CheckForPredefinedName(fullTypeName.Substring(0, indexOfBracket));
					var isReflectedType = (typeNameOnly.IndexOf("+") > -1);
					var listOfGenericParams = 
						fullTypeName.Substring(indexOfBracket)
						.RemoveOneCharFromStartAndEnd()
						.SplitWithCommaBySquareBrackets()
						.Select(RemoveOneCharFromStartAndEnd)
						.Select(ToShortTypeName);
					typename.Append(isReflectedType ? typeNameOnly : typeNameOnly.Substring(0, typeNameOnly.Length - 2));
					typename.Append("<");
					typename.Append(string.Join(",", listOfGenericParams.ToArray()));
					typename.Append(">");
				}
			}
			else
			{
				typename.Append(CheckForPredefinedName(fullTypeName));
			}
			string typenameStr = typename.ToString();
			//nameCache.Add(type, typenameStr);
			return typenameStr;
		}

		private static string RemoveOneCharFromStartAndEnd(this string str)
		{
			return str.Substring(1, str.Length - 2);
		}

		private static string ConvertArrayRank(string arrayRank)
		{
			var str = "";
			var ranks = arrayRank.Split('[').Skip(1);
			foreach (var rank in ranks)
			{
				str += "[" + (rank.Count(c => c == ',')+1) + "]";
			}
			return str;
		}

		private static string CheckForPredefinedName(string fullTypeName)
		{
			string predefinedName = null;
			if (predefinedNames.TryGetValue(fullTypeName, out predefinedName))
				return predefinedName;
			return fullTypeName;
		}

		private static string ExtractFullNameFromFullyQualifiedName(string fullyQualifiedName)
		{
			var parts = SplitWithCommaBySquareBrackets(fullyQualifiedName);
			return parts.First();
		}

		internal static IEnumerable<string> SplitWithCommaBySquareBrackets(this string str)
		{
			return SplitByBrackets(str, ',', '[', ']');
		}

		internal static IEnumerable<string> SplitByBrackets(string str, char token, char openingBracket, char closingBracket)
		{
			int level = 0;
			int lastTokenPos = -1;
			for (int i = 0; i < str.Length; i++)
			{
				var c = str[i];
				if (c == openingBracket)
					level++;
				else if (c == closingBracket)
					level--;
				else if (c == token && level == 0)
				{
					yield return str.Substring(lastTokenPos + 1, i - (lastTokenPos + 1));
					lastTokenPos = i;
				}
			}
			yield return str.Substring(lastTokenPos + 1); //, str.Length - (lastTokenPos + 1));		
		}
	}
}
